//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/symtab/set_uint64.h"
#include "sagittarius/io/io.h"
#include <stddef.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

/// check
int set_uint64_check(set_uint64* ht, uint64 e) {
    set_uint64_bucket* b = &ht->buckets[e%ht->nbuckets];
    uint64 i;
    
    for(i=0; i<b->n; ++i) { 
        if(b->e[i] == e) {
            return 1;
        }
    }
    
    return 0;
}

//This is why we have operator overloading
/*int ____valuecompare(uint64 p, uint64 q) {
    return p == q;
}*/

/// insert
int set_uint64_insert(set_uint64* ht, uint64 e) {
    set_uint64_bucket* b = NULL;
    size_t bn;
    uint64* tmp;
    uint64 i;
    uint64 depth;
    
    bn = e%ht->nbuckets;
    b = &ht->buckets[bn];
    
    for(i=0; i<b->n; ++i) {
        if(b->e[i] == e) {
            return HASH_COLLISION;
        }
    }
    if(b->n >= b->depth) {
        //need to reallocate
        depth = b->depth<<1;
        tmp = malloc(depth*sizeof(uint64));
        memcpy(tmp, b->e, b->depth*sizeof(uint64));
        b->depth = depth;
        free(b->e);
        b->e = tmp;
    }
    b->e[b->n] = e;
    ++b->n;
    
    
    return HASH_SUCCESS;
}

/// make_unique
uint64 set_uint64_make_unique(set_uint64* ht) {
    uint64 i;
    do {
        i = rand(); //TODO: better generator than rand
    } while(set_uint64_check(ht, i));
    return i;
}

/// rehash
set_uint64* set_uint64_rehash(set_uint64* ht, uint64 nbuckets, uint64 depth) {
    set_uint64* h;
    set_uint64_bucket* b;
    uint64 i, j;
    
    h = malloc(sizeof(set_uint64));
    AN(h);
    
    h->nbuckets = nbuckets;
    h->buckets = malloc(nbuckets*sizeof(set_uint64_bucket));
    for(i=0; i<h->nbuckets; ++i) {
        h->buckets[i].n = 0;
        h->buckets[i].depth = depth;
        h->buckets[i].e = malloc(depth*sizeof(uint64));
        
    }
    
    //insert old values
    if(ht != NULL) {
        for(i=0; i<ht->nbuckets; ++i) {
            b = &ht->buckets[i];
            for(j=0; j<b->n; ++j) {
                set_uint64_insert(h, b->e[j]);
            }
        }
    }
    
    return h;
}

void set_uint64_bucket_destoy(set_uint64_bucket* b) {
    free(b->e);
}

void set_uint64_destroy(set_uint64* h) {
    uint64 i;
    for(i=0; i<h->nbuckets; ++i) {
        set_uint64_bucket_destoy(&h->buckets[i]);
    }
    free(h->buckets);
    free(h);
}

void set_uint64_dumpstate(int fd, set_uint64* h) {
    uint64 i, j;
    SG_WRITE(fd, "uint64 Set");
    SG_WRITE(fd, "\n  Number of buckets: ");
    sg_writeInt(fd, (int)h->nbuckets);
    SG_WRITE(fd, "\n");
    for(i=0; i<h->nbuckets; ++i) {
        SG_WRITE(fd, "    Bucket ");
        sg_writeUInt(fd, i);
        SG_WRITE(fd, "\n      Number ");
        sg_writeUInt(fd, h->buckets[i].n);
        SG_WRITE(fd, "\n      Depth ");
        sg_writeUInt(fd, h->buckets[i].depth);
        SG_WRITE(fd, "\n");
        
        for(j=0; j<h->buckets[i].n; ++j) {
            SG_WRITE(fd, "        Entry ");
            sg_writeUInt64Hex(fd, h->buckets[i].e[j]);
            SG_WRITE(fd, "\n");
        }
    }
}